package xin.nick.common.aspect;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import xin.nick.common.entity.LogRequestInfo;
import xin.nick.common.util.IpUtil;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;

/**
 * 请求参数打印
 * @author Nick
 * @since 2022/1/24
 */
@Slf4j
@Aspect
@Configuration
public class AopLogAspect {

    public AopLogAspect() {
       log.info("日志切面实例化");
    }


    /**
     * 这里是全部切面
     */
    @Pointcut("execution(public * xin.nick.controller..*.*(..))")
    public void webLog() {
    }

    /**
     * 根据注解进行切面
     */
    @Pointcut("@annotation(xin.nick.common.annotation.AopLog)")
    public void webLogAnnotation() {
    }

    @Around("webLogAnnotation()")
    public Object handleByAnnotation(ProceedingJoinPoint joinPoint) throws Throwable {

        try {
            log.info("请求日志记录开始:");
            LogRequestInfo logRequestInfo = preHandleV2(joinPoint);
            log.info(logRequestInfo.toString());
        } catch(Exception e) {
            log.warn("打印输入参数出错: {}", e.getMessage());
        }

        long begin = System.currentTimeMillis();

        // 原方法
        Object result = joinPoint.proceed();

        try {
            log.info("REQUEST DURATION(AOP)     : {} ms", (System.currentTimeMillis() - begin));
            log.info("REQUEST RESULT(AOP)       : {}", JSON.toJSONString(result));
            log.info("请求日志记录结束");
        } catch(Exception e) {
            log.warn("打印返回参数出错: {}", e.getMessage());
        }


        return result;
    }


    @Around("webLog()")
    public Object handleByAll(ProceedingJoinPoint joinPoint) throws Throwable {

        try {
            log.debug("请求日志记录开始(AOP) :");
            LogRequestInfo logRequestInfo = preHandleV2(joinPoint);
            log.debug(logRequestInfo.toString());
        } catch(Exception e) {
            log.warn("打印输入参数出错(AOP) : {}", e.getMessage());
        }

        long begin = System.currentTimeMillis();

        // 原方法
        Object result = joinPoint.proceed();

        try {
            log.debug("REQUEST DURATION(AOP)     : {} ms", (System.currentTimeMillis() - begin));
            log.debug("REQUEST RESULT(AOP)       : {}", JSON.toJSONString(result));
            log.debug("请求日志记录结束(AOP) ");
        } catch(Exception e) {
            log.warn("打印返回参数出错(AOP) : {}", e.getMessage());
        }

        return result;
    }


    /**
     * 入参数据
     *
     * @param joinPoint 切点
     * @param request 请求
     * @return 返回请求参数
     */
    private String preHandle(ProceedingJoinPoint joinPoint, HttpServletRequest request) {

        String reqParam = "";
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        Annotation[] annotations = targetMethod.getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation.annotationType().equals(RequestMapping.class) ||
                    annotation.annotationType().equals(GetMapping.class) ||
                    annotation.annotationType().equals(PostMapping.class) ||
                    annotation.annotationType().equals(PutMapping.class) ||
                    annotation.annotationType().equals(DeleteMapping.class)) {
                reqParam = JSON.toJSONString(request.getParameterMap());
                break;
            }
        }
        return reqParam;
    }

    private LogRequestInfo preHandleV2(ProceedingJoinPoint joinPoint) {

        LogRequestInfo logRequestInfo = new LogRequestInfo();

        // 准备取出参数信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        Signature signature = joinPoint.getSignature();
        String method = request.getMethod();
        String contentType = request.getContentType();
        String paramString;
        if (HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) {
            Map<String, String[]> parameterMap = request.getParameterMap();
            paramString = JSON.toJSONString(parameterMap);
        } else if (Objects.equals(contentType, MediaType.APPLICATION_JSON_VALUE)) {
            paramString = JSON.toJSONString(joinPoint.getArgs()[0]);
        } else {
            paramString = "其他内容";
        }
        String ipAddress= IpUtil.getIpAddress(request);

        // 组装好返回的内容
        logRequestInfo.setLogType("AOP");
        logRequestInfo.setIp(ipAddress);
        logRequestInfo.setUrl(request.getRequestURL().toString());
        logRequestInfo.setUri(request.getRequestURI());
        logRequestInfo.setMethod(method);
        logRequestInfo.setTypeName(signature.getDeclaringType().toString());
        logRequestInfo.setMethodName(signature.getName());
        logRequestInfo.setUserAgent(request.getHeader("User-Agent"));
        logRequestInfo.setContentType(contentType);
        logRequestInfo.setParam(paramString);

        return logRequestInfo;

    }

    /**
     * 返回数据
     *
     * @param retVal 返回数据对象
     * @return 返回结果转换为JSON字符串
     */
    private String postHandle(Object retVal) {
        if (null == retVal) {
            return "";
        }
        return JSON.toJSONString(retVal);
    }


}
